package com.hwadee.property.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hwadee.property.util.AjaxResult;
import com.hwadee.property.util.JwtUtil;
import io.jsonwebtoken.JwtException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.regex.Pattern;

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Resource
    private JwtUtil jwtUtil;

    private final ObjectMapper objectMapper = new ObjectMapper(); // 用于序列化 AjaxResult

    // 物业可以访问的接口前缀
    private static final Set<String> PROPERTY_MANAGEMENT_PREFIXES = new HashSet<>(Arrays.asList(
            "/api/users/personal", // 物业查看个人信息
            "/api/users/personalUpdate", // 物业修改个人信息
            "/api/users/property/page", // 物业分页查询业主信息
            "/api/users/updateUser", // 物业管理用户信息
            "/api/users/freeze", // 物业冻结用户账户
            "/api/notice/property", // 物业分页查询通知、发布通知
            "/api/repair/property", // 物业分页查询所在小区的报修信息、处理报修
            "/api/complain/property", // 物业分页查询所在小区的投诉或建议
            "/api/charge-items/property", // 物业新增收费项目、分页查看收费项目列表、修改收费项目、删除收费项目
            "/api/parking/property", // 物业增加、修改、删除车位，分页查询车位
            "/api/parking-use/property", // 物业查询车位使用记录
            "/api/charge-details/property" // 物业生成物业费账单、查询物业账单
    ));

    // 物业可以访问的动态路径模式
    private static final Pattern[] PROPERTY_MANAGEMENT_PATTERNS = {
            Pattern.compile("/api/notice/\\d+") // 物业更新通知
    };

    // 业主可以访问的接口前缀
    private static final Set<String> OWNER_PREFIXES = new HashSet<>(Arrays.asList(
            "/api/users/personal", // 业主查看个人信息
            "/api/users/personalUpdate", // 业主修改个人信息
            "/api/notice/personal", // 业主查看本人房产所属小区物业通知
            "/api/charge-details/personal", // 业主查看需缴物业费
            "/api/alipay/pay", // 业主缴纳物业费
            "/api/parking-use/personal", // 业主查看个人车位使用记录
            "/api/parking-use/applyForParking", // 业主申请车位
            "/api/complain/personal", // 业主查看个人投诉与建议记录
            "/api/houses/personal", // 业主查看个人名下房产
            "/api/community/personal", // 业主查看个人名下房产所属小区
            "/api/repair/personal" // 业主查看个人报修记录、提交报修请求
    ));

    // 业主可以访问的动态路径模式
    private static final Pattern[] OWNER_PATTERNS = {
            Pattern.compile("/api/repair/cancel/\\d+"), // 业主取消报修
            Pattern.compile("/api/complain/\\d+") // 业主提交投诉建议
    };

    /**
     * 拦截器功能代码
     * @param request 请求
     * @param response  响应
     * @param handler 使用的handler
     * @return 是否放行
     * @throws Exception 出错
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("uri:" + request.getRequestURI());
        String method = request.getMethod();
        if ("OPTIONS".equals(method)) {
            return true; // 放行 OPTIONS 请求
        }
        // 放行支付请求
        if (request.getRequestURI().startsWith("/api/alipay")) return true;

        // 放行文件获取请求
        if (request.getRequestURI().startsWith("/api/file")) return true;

        // 放行 swagger 文档请求
        if (request.getRequestURI().startsWith("/doc.html")) return true;
        if (request.getRequestURI().startsWith("/webjars")) return true;
        if (request.getRequestURI().startsWith("/swagger-resources")) return true;

        // 放行根据id查看通知详情请求
        if (request.getRequestURI().startsWith("/api/notice/all")) return true;

        // 放行登录请求
        if (request.getRequestURI().matches("/api/users/login")) return true;
        if (request.getRequestURI().matches("/api/administrators/login")) return true;

        // 获取 Token
        String token = request.getHeader("Authorization");
        if (!StringUtils.hasLength(token)) {
            writeResponse(response, HttpServletResponse.SC_UNAUTHORIZED, AjaxResult.error(401, "Token 不能为空"));
            return false;
        }

        // 验证 Token
        try {
            jwtUtil.verifyToken(token);
        } catch (JwtException e) {
            writeResponse(response, HttpServletResponse.SC_FORBIDDEN, AjaxResult.error(e.getMessage()));
            return false;
        }

        // 获取 Token 解析信息
        Map<String, Object> claims = jwtUtil.getSubject(token);
        Object roleObj = claims.get("role");
        int role = ((Number) roleObj).intValue(); // 获取用户角色
        String requestUri = request.getRequestURI();

        // 定义不同角色的权限
        if (role == 0) return true;

        if (isPropertyManagementEndpoint(requestUri)
                && 2 != role
                && !Objects.equals(requestUri, "/api/users/personal")
                && !Objects.equals(requestUri, "/api/users/personalUpdate")) {
            writeResponse(response, HttpServletResponse.SC_FORBIDDEN, AjaxResult.error(403, "无权限访问该接口"));
            return false;
        }

        if (isOwnerEndpoint(requestUri)
                && 1!=role
                && !Objects.equals(requestUri, "/api/users/personal")
                && !Objects.equals(requestUri, "/api/users/personalUpdate")) {
            writeResponse(response, HttpServletResponse.SC_FORBIDDEN, AjaxResult.error(403, "无权限访问该接口"));
            return false;
        }

        return true;
    }


    /**
     * 统一封装 JSON 响应
     */
    private void writeResponse(HttpServletResponse response, int status, AjaxResult result) throws Exception {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(result));
    }

    /**
     * 判断是否是物业可以访问的接口
     *
     * @param uri 被判定的接口地址
     * @return 是否可以访问
     */
    public boolean isPropertyManagementEndpoint(String uri) {
        return checkEndpoint(uri, PROPERTY_MANAGEMENT_PREFIXES, PROPERTY_MANAGEMENT_PATTERNS);
    }

    /**
     * 判断是否是业主可以访问的接口
     *
     * @param uri 被判定的接口地址
     * @return 是否可以访问
     */
    public boolean isOwnerEndpoint(String uri) {
        return checkEndpoint(uri, OWNER_PREFIXES, OWNER_PATTERNS);
    }

    /**
     * 通用的接口权限检查方法
     *
     * @param uri              被判定的接口地址
     * @param allowedPrefixes  允许的前缀集合
     * @param allowedPatterns  允许的动态路径模式
     * @return 是否可以访问
     */
    private boolean checkEndpoint(String uri, Set<String> allowedPrefixes, Pattern[] allowedPatterns) {
        // 检查是否匹配动态路径模式
        for (Pattern pattern : allowedPatterns) {
            if (pattern.matcher(uri).matches()) {
                return true;
            }
        }

        // 检查是否以允许的固定前缀开头
        for (String prefix : allowedPrefixes) {
            if (uri.startsWith(prefix)) {
                return true;
            }
        }

        return false;
    }
}
